//===--- FormalEvaluation.cpp ---------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/gen/FormalEvaluation.h"
#include "polarphp/pil/gen/LValue.h"
#include "polarphp/pil/gen/PILGenFunction.h"

using namespace polar;
using namespace lowering;

//===----------------------------------------------------------------------===//
//                               Formal Access
//===----------------------------------------------------------------------===//

void FormalAccess::_anchor() {}

void FormalAccess::verify(PILGenFunction &SGF) const {
#ifndef NDEBUG
   // If this access was already finished, continue. This can happen if an
   // owned formal access was forwarded.
   if (isFinished()) {
      assert(getKind() == FormalAccess::Owned &&
             "Only owned formal accesses should be forwarded.");
      // We can not check that our cleanup is actually dead since the cleanup
      // may have been popped at this point and the stack may have new values.
      return;
   }

   assert(!isFinished() && "Can not finish a formal access cleanup "
                           "twice");

   // Now try to look up the cleanup handle of the formal access.
   SGF.Cleanups.checkIterator(getCleanup());
#endif
}

//===----------------------------------------------------------------------===//
//                      Shared Borrow Formal Evaluation
//===----------------------------------------------------------------------===//

void SharedBorrowFormalAccess::finishImpl(PILGenFunction &SGF) {
   SGF.B.createEndBorrow(CleanupLocation::get(loc), borrowedValue,
                         originalValue);
}

//===----------------------------------------------------------------------===//
//                             OwnedFormalAccess
//===----------------------------------------------------------------------===//

void OwnedFormalAccess::finishImpl(PILGenFunction &SGF) {
   auto cleanupLoc = CleanupLocation::get(loc);
   if (value->getType().isAddress())
      SGF.B.createDestroyAddr(cleanupLoc, value);
   else
      SGF.B.emitDestroyValueOperation(cleanupLoc, value);
}

//===----------------------------------------------------------------------===//
//                          Formal Evaluation Scope
//===----------------------------------------------------------------------===//

FormalEvaluationScope::FormalEvaluationScope(PILGenFunction &SGF)
   : SGF(SGF), savedDepth(SGF.FormalEvalContext.stable_begin()),
     previous(SGF.FormalEvalContext.innermostScope),
     wasInInOutConversionScope(SGF.InInOutConversionScope) {
   if (wasInInOutConversionScope) {
      savedDepth.reset();
      assert(isPopped());
      return;
   }
   SGF.FormalEvalContext.innermostScope = this;
}

FormalEvaluationScope::FormalEvaluationScope(FormalEvaluationScope &&o)
   : SGF(o.SGF), savedDepth(o.savedDepth), previous(o.previous),
     wasInInOutConversionScope(o.wasInInOutConversionScope) {

   // Replace the scope in the active-scope chain if it's present.
   if (!o.isPopped()) {
      for (auto c = &SGF.FormalEvalContext.innermostScope; ; c = &(*c)->previous){
         if (*c == &o) {
            *c = this;
            break;
         }
      }
   }

   o.savedDepth.reset();
   assert(o.isPopped());
}

void FormalEvaluationScope::popImpl() {
   auto &context = SGF.FormalEvalContext;

   // Remove the innermost scope from the chain.
   assert(context.innermostScope == this &&
          "popping formal-evaluation scopes out of order");
   context.innermostScope = previous;

   auto endDepth = *savedDepth;

   // Check to see if there is anything going on here.
   if (endDepth == context.stable_begin())
      return;

#ifndef NDEBUG
   // Verify that all the accesses are valid.
   for (auto i = context.begin(), e = context.find(endDepth); i != e; ++i) {
      i->verify(SGF);
   }
#endif

   // Save our start point to make sure that we are not adding any new cleanups
   // to the front of the stack.
   auto originalBegin = context.stable_begin();

   // Then working down the stack until we visit unwrappedSavedDepth...
   auto i = originalBegin;
   do {
      // Grab the next evaluation.
      FormalAccess &access = context.findAndAdvance(i);

      // If this access was already finished, continue. This can happen if an
      // owned formal access was forwarded.
      if (access.isFinished()) {
         assert(access.getKind() == FormalAccess::Owned &&
                "Only owned formal accesses should be forwarded.");
         // We can not check that our cleanup is actually dead since the cleanup
         // may have been popped at this point and the stack may have new values.
         continue;
      }

      assert(!access.isFinished() && "Can not finish a formal access cleanup "
                                     "twice");

      // Set the finished bit to appease various invariants.
      access.setFinished();

      // Deactivate the cleanup.
      SGF.Cleanups.setCleanupState(access.getCleanup(), CleanupState::Dead);

      // Attempt to diagnose problems where obvious aliasing introduces illegal
      // code. We do a simple N^2 comparison here to detect this because it is
      // extremely unlikely more than a few writebacks are active at once.
      if (access.getKind() == FormalAccess::Exclusive) {
         // Note that we already advanced 'iter' above, so we can just start
         // iterating from there.  Also, this doesn't invalidate the iterators.
         for (auto j = context.find(i), je = context.find(endDepth); j != je; ++j){
            FormalAccess &other = *j;
            if (other.getKind() != FormalAccess::Exclusive)
               continue;
            auto &lhs = static_cast<ExclusiveBorrowFormalAccess &>(access);
            auto &rhs = static_cast<ExclusiveBorrowFormalAccess &>(other);
            lhs.diagnoseConflict(rhs, SGF);
         }
      }

      // Claim the address of each and then perform the writeback from the
      // temporary allocation to the source we copied from.
      //
      // This evaluates arbitrary code, so it's best to be paranoid
      // about iterators on the context.
      DiverseValueBuffer<FormalAccess> copiedAccess(access);
      copiedAccess.getCopy().finish(SGF);

   } while (i != endDepth);

   // Then check that we did not add any additional cleanups to the beginning of
   // the stack...
   assert(originalBegin == context.stable_begin() &&
          "pushed more formal evaluations while popping formal evaluations?!");

   // And then pop off all stack elements until we reach the savedDepth.
   context.pop(endDepth);
}

void FormalEvaluationScope::verify() const {
   // Walk up the stack to the saved depth.
   auto &context = SGF.FormalEvalContext;
   for (auto i = context.begin(), e = context.find(*savedDepth); i != e; ++i) {
      i->verify(SGF);
   }
}

//===----------------------------------------------------------------------===//
//                         Formal Evaluation Context
//===----------------------------------------------------------------------===//

void FormalEvaluationContext::dump(PILGenFunction &SGF) {
   for (auto II = begin(), IE = end(); II != IE; ++II) {
      FormalAccess &access = *II;
      SGF.Cleanups.dump(access.getCleanup());
   }
}
